package com.cx.auth.config;

import com.cx.auth.AuthConstant;
import com.cx.auth.filter.JWTAuthenticationFilter;
import com.cx.auth.impl.UserDetailServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.annotation.Resource;

/**
 * Security配置类
 * Author:CHENXIAOYI
 * Since:2020/12/25 9:18
 */
@Configuration
@ConditionalOnClass(UserDetails.class)
@Import(SecurityHandlerConfig.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Resource
    private UserDetailServiceImpl userDetailService;

    /*登录失败处理器*/
    @Resource
    private AuthenticationFailureHandler failureHandler;

    /*登录成功处理器*/
    @Resource
    private AuthenticationSuccessHandler successHandler;

    /*登出成功处理器*/
    @Resource
    private LogoutSuccessHandler logoutSuccessHandler;


    /**
     * 定义密码加密器
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置用户授权方法
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService);
    }


    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        //关闭 AbstractUserDetailsAuthenticationProvider(line69)中 UsernameNotFoundException 转为 BadCredentialsException
        // 使手动抛出 UsernameNotFoundException 的异常信息能友好提示
        provider.setHideUserNotFoundExceptions(false);
        provider.setUserDetailsService(userDetailService);
        return provider;
    }

    /**
     * 配置请求
     * @see UsernamePasswordAuthenticationFilter 默认登录名密码
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*签发token后需要禁用缓存  用了token所以禁用csrf*/
        http.headers().cacheControl().disable()
                .and().headers().frameOptions().disable()//允许使用iframe嵌套,避免swagger-ui不被加载的问题
                .and().csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); //无session
        //设置前置过滤器
        http.addFilterBefore(new JWTAuthenticationFilter(authenticationManager()),UsernamePasswordAuthenticationFilter.class);
        /*登陆配置*/
        http.formLogin()
        .loginProcessingUrl(AuthConstant.LOGIN_URL) //登陆执行路径 不需要自己实现
        .usernameParameter(AuthConstant.USER_NAME) //设置入参对象的登录名 不设置必填为username
        .passwordParameter(AuthConstant.PASS_WORD) //设置入参对象的密码 不设置必填为password
        .successHandler(successHandler) //成功处理器
        .failureHandler(failureHandler) //失败处理器
        .permitAll();

        /*登出配置*/
        http.logout().logoutUrl(AuthConstant.LOGOUT_URL) // 登出默认路径 /logout
        .logoutSuccessHandler(logoutSuccessHandler);

        /*请求放行*/
        http.authorizeRequests()
                .antMatchers(AuthConstant.PERMIT_URLS).permitAll()
                .antMatchers("^/.*$").hasRole("0") // 超级管理员可访问任何页面
                .anyRequest().authenticated();   // 所有的请求都需要登录才能访问
    }

    /**
     * web资源放行
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().mvcMatchers("/static/*");
    }
}
